package luj.game.robot.internal.start;

import luj.ava.reflect.type.TypeX;
import luj.cluster.api.actor.Tellable;
import luj.cluster.api.node.NodeStartListener;
import luj.game.robot.api.network.RobotProtoHandler;
import luj.game.robot.internal.admin.actor.BotAdminActor;
import luj.game.robot.internal.concurrent.instance.RobotInstanceDependency;
import luj.game.robot.internal.concurrent.instance.command.map.CommandMap;
import luj.game.robot.internal.concurrent.instance.command.map.CommandMapFactory;
import luj.game.robot.internal.concurrent.parent.RobotBossDependency;
import luj.game.robot.internal.concurrent.parent.RobotParentActor;
import luj.game.robot.internal.dynamic.combine.AllBeanCombiner;
import luj.game.robot.internal.instance.boss.command.map.BossCmdMapFactory;
import luj.game.robot.internal.instance.boss.command.map.BossCommandMap;
import luj.game.robot.internal.instance.boss.state.BossState;
import luj.game.robot.internal.net.http.collect.HandleMap;
import luj.game.robot.internal.net.http.collect.HandleMapMaker;
import luj.game.robot.internal.start.listener.BotStartListenTrigger;
import luj.spring.anno.Internal;

import java.util.Map;
import java.util.function.Function;

import static java.util.stream.Collectors.toMap;

@Internal
final class OnLujclusterStart implements NodeStartListener {

  @Override
  public void onStart(Context ctx) {
    BotbeanInLujcluster botbean = ctx.getStartParam();

    AllBeanCombiner.Result injectRoot = botbean.getInjectRoot();
    HandleMap httpMap = new HandleMapMaker(injectRoot).make();
    var adminActor = new BotAdminActor(httpMap);
    Tellable adminRef = ctx.createApplicationActor(adminActor);

    RobotBossDependency bossDep = collectBossDependency(botbean);
    RobotInstanceDependency workerDep = collectWorkerDependency(botbean);

    var parentActor = new RobotParentActor(new BossState(), bossDep, workerDep, adminRef);
    Actor parentRef = ctx.createApplicationActor(parentActor);
    adminActor.setBossRef(parentRef);
    adminActor.setBossDependency(bossDep);

    new BotStartListenTrigger(injectRoot.startListener(), parentRef, adminRef, httpMap).trigger();
  }

  private RobotBossDependency collectBossDependency(BotbeanInLujcluster botbean) {
    AllBeanCombiner.Result rootBean = botbean.getInjectRoot();
    BossCommandMap cmdMap = new BossCmdMapFactory(rootBean).create();

    return new RobotBossDependency(cmdMap, botbean.getLujbean());
  }

  private RobotInstanceDependency collectWorkerDependency(BotbeanInLujcluster botbean) {
    AllBeanCombiner.Result rootBean = botbean.getInjectRoot();
    CommandMap cmdMap = new CommandMapFactory(rootBean).create();

    Map<String, RobotProtoHandler<?>> handleMap = rootBean.protoHandler().stream()
        .collect(toMap(this::getProtoType, Function.identity()));

    return new RobotInstanceDependency(rootBean.instanceRoot(),
        botbean.getLujbean(), rootBean.createListener(), cmdMap, handleMap);
  }

  private String getProtoType(RobotProtoHandler<?> handler) {
    return TypeX.ofInstance(handler)
        .getSupertype(RobotProtoHandler.class)
        .getTypeParam(0)
        .asClass()
        .getName();
  }
}
